import datetime
import time
from collections import defaultdict

import latexify
from scipy.optimize import curve_fit
import numpy as np
import torch
from fastapi import APIRouter, Header
from torch import nn, optim

from api.experiment.water import water_router
from model.db import connect
from model.db.connect import tenant_session_scope
from model.db.mapper.experiment_mapper import get_water_experiment_list
from model.db.mapper.model_mapper import get_model_list, get_model_paras, get_model_data, add_model, \
    update_model_inputParas, get_model_SampleData, update_model_outputParas, get_model_trainData, \
    update_bpmodel_stucture, get_model, get_model_testData, get_bpmodel_structure, get_model_predictData, \
    update_model_trainData, update_model_testData, get_model_train_para, update_model_predictData, update_model, \
    delete_model, get_multi_model_paras, update_SOMmodel_ParasRelation, update_SOMmodel_structure, \
    get_sommodel_structure
from model.db.mapper.well_mapper import update_core_data
from model.params.experiment_params import GetExperimentParam
from model.params.model_params import ModelIDParam, ModelNameParam, ModelParamUpdate, ModelTrainParam, ModelTypeParam, \
    ModelTrainDataUpdate, UpdateParam, ModelList
from util import success_res, objs_to_dict, SuccessCode, obj_to_dict, error_res, ErrorCode
from util.BPNN import CustomizableBPNN
from util.SOMModel import liner, quad, cube, liner_latex, quad_latex, cube_latex, exponent, exponent_latex, exponent2, \
    exponent2_latex, power, power_latex
from util.public_func import compute_evaluation_index, transform_Datasets, datasets_to_tensor, discard_Core, \
    modified_data, datasets_to_tensor_split

model_router = APIRouter(prefix="/model", tags=['模型'])


@model_router.post("/add", name="新增模型")
async def model_list(param: ModelNameParam, token=Header('token')):
    model = add_model(param.model_name, param.model_type, param.model_description, param.user_id,
                      param.sensitivity_type)
    return success_res(obj_to_dict(model), SuccessCode.GET)


@model_router.post("/list", name="模型列表")
async def model_list(parm: ModelTypeParam, token=Header('token')):
    models = get_model_list(parm.Model_Type, parm.Sensitivity_Type)
    return success_res(objs_to_dict(models), SuccessCode.GET)


@model_router.post("/info", name="获取模型")
async def model_list(parm: ModelIDParam, token=Header('token')):
    models = get_model(parm.model_id)
    return success_res(obj_to_dict(models), SuccessCode.GET)


@model_router.post("/update", name="模型信息修改")
async def model_list(parm: UpdateParam, token=Header('token')):
    models = update_model(parm.model_id, parm.model_name, parm.model_description)
    return success_res("", SuccessCode.PUT)


@model_router.post("/delete", name="模型删除")
async def model_list(parm: ModelIDParam, token=Header('token')):
    models = delete_model(parm.model_id)
    return success_res("", SuccessCode.DELETE)


@model_router.post("/params", name="模型参数")
async def model_paras(param: ModelIDParam, token=Header('token')):
    inputParams, outputParams = get_model_paras(param.model_id)
    return success_res({"input_params": objs_to_dict(inputParams), "out_putParams": objs_to_dict(outputParams)},
                       SuccessCode.GET)


@model_router.post("/multi_params", name="复合模型参数最大集")
async def model_paras(param: ModelList, token=Header('token')):
    inputParams, outputParams = get_multi_model_paras(param.model_list)
    return success_res({"input_params": objs_to_dict(inputParams), "out_putParams": objs_to_dict(outputParams)},
                       SuccessCode.GET)


@model_router.post("/update_inputParams", name="更新模型输入参数")
async def update_model_paras(param: ModelParamUpdate, token=Header('token')):
    newAdd = update_model_inputParas(param.add, param.delete, param.update)
    return success_res(newAdd, SuccessCode.PUT)


@model_router.post("/update_SOMParams", name="更新自组织模型参数关系")
async def update_model_paras(param: ModelParamUpdate, token=Header('token')):
    update_SOMmodel_ParasRelation(param.update)
    return success_res("", SuccessCode.PUT)


@model_router.post("/update_outputParams", name="更新模型输出参数")
async def update_model_paras(param: ModelParamUpdate, token=Header('token')):
    newAdd = update_model_outputParas(param.add, param.delete, param.update)
    return success_res(newAdd, SuccessCode.PUT)


@model_router.post("/sampleData", name="模型样本数据")
async def get_model_sample(param: ModelIDParam, token=Header('token')):
    train_data, test_data = get_model_SampleData(param.model_id)

    modified_train_data = []
    for item in train_data:
        core_id = item["Core_ID"]
        core_name = item["Core_Name"]
        parameter_ecode = item["Parameter_Ecode"]
        parameter_value = item["Parameter_Value"]

        if core_id not in [x["Core_ID"] for x in modified_train_data]:
            modified_train_data.append({"Core_ID": core_id, "Core_Name": core_name})

        core_data = next((x for x in modified_train_data if x["Core_ID"] == core_id), None)
        core_data[parameter_ecode] = parameter_value
    modified_test_data = []
    for item in test_data:
        core_id = item["Core_ID"]
        core_name = item["Core_Name"]
        parameter_ecode = item["Parameter_Ecode"]
        parameter_value = item["Parameter_Value"]

        if core_id not in [x["Core_ID"] for x in modified_test_data]:
            modified_test_data.append({"Core_ID": core_id, "Core_Name": core_name})

        core_data = next((x for x in modified_test_data if x["Core_ID"] == core_id), None)
        core_data[parameter_ecode] = parameter_value

    return success_res({"train_data": sorted(objs_to_dict(modified_train_data), key=lambda x: x["Core_ID"]),
                        "test_data": sorted(objs_to_dict(modified_test_data), key=lambda x: x["Core_ID"])},
                       SuccessCode.GET)


@model_router.post("/updateTrain", name="更新训练集")
async def get_model_sample(param: ModelTrainDataUpdate, token=Header('token')):
    add = update_model_trainData(param.add, param.delete, param.update)
    return success_res(add, SuccessCode.PUT)


@model_router.post("/updateTest", name="更新测试集")
async def get_model_sample(param: ModelTrainDataUpdate, token=Header('token')):
    add = update_model_testData(param.add, param.delete, param.update)
    return success_res(add, SuccessCode.PUT)


@model_router.post("/sampleDataAnalysis", name="模型样本数据分析")
async def sampleDataAnaly(param: ModelIDParam, token=Header('token')):
    train_data, test_data = get_model_SampleData(param.model_id)
    train_index = compute_evaluation_index(train_data)
    test_index = compute_evaluation_index(test_data)
    return success_res({"train_data": train_index, "test_data": test_index}, SuccessCode.GET)


@model_router.post("/train", name="模型训练")
async def model_train(param: ModelTrainParam, token=Header('token')):
    try:
        X, Y = get_model_trainData(param.model_id)
        X_train, Y_train = datasets_to_tensor(X, Y)
        # 对输入和输出进行标准化（可以根据具体情况选择标准化或归一化）
        # X_mean = X_train.mean(dim=0)
        # X_std = X_train.std(dim=0)
        # X_train = (X_train - X_mean) / X_std
        #
        # Y_mean = Y_train.mean(dim=0)
        # Y_std = Y_train.std(dim=0)
        # Y_train = (Y_train - Y_mean) / Y_std
        # 创建BP神经网络
        input_size = X_train.size()[1]  # 输入层神经元个数
        output_size = Y_train.size()[1]  # 输出层神经元个数
        hidden_layers = param.Hidden_layers  # 隐藏层数
        neurons_per_layer = param.HideLayer_CoreNum  # 隐藏层神经元个数
        target_error = param.Target_error  # 目标误差
        epochs = param.MaxEpoch  # 最大迭代次数
        learn_rate = param.Learn_Rate  # 学习率
        activation_function = param.HideLayer_Active  # 可选 'sigmoid', 'relu', 'tanh' 等
        Real_Epoch = 0  # 实际迭代数
        Result_Error = -1  # 最终误差
        loss_history = []  # 保存每次迭代的误差历史
    except Exception as e:
        return error_res(ErrorCode.DATA_ERROR)
    try:
        bpnn = CustomizableBPNN(input_size, output_size, hidden_layers, neurons_per_layer, activation_function)

        # 定义损失函数和优化器
        criterion = nn.MSELoss()
        optimizer = optim.Adam(bpnn.parameters(), lr=learn_rate)
        starttime = time.time()
        # 训练BP神经网络
        for epoch in range(epochs):
            optimizer.zero_grad()
            outputs = bpnn(X_train)
            loss = criterion(outputs, Y_train)
            loss.backward()
            optimizer.step()
            # 保存每次迭代的误差值
            loss_history.append(float('{:.6f}'.format(loss.item())))
            print(f"Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.6f}")
            Real_Epoch = epoch
            Result_Error = loss.item()
            if loss.item() < target_error:
                print(
                    f"Training stopped at epoch {epoch + 1}, because loss ({loss.item():.6f}) is less than target error ({target_error}).")
                break
    except Exception as e:
        print(e)
        return error_res(ErrorCode.Param_ERROR)
    # 计算训练时长
    endtime = time.time()
    dtime = endtime - starttime
    Epoch_Duration = float(dtime)
    # 格式化时间间隔为小时、分钟、秒
    # 保存模型权重
    fileName = "BPModel_" + str(param.model_id) + "_" + datetime.datetime.now().strftime('%Y%m%d%H%M%S')
    torch.save(bpnn.state_dict(), './weight/' + fileName + '.pth')
    # 结果
    result = {"Model_ID": param.model_id, "Real_Epoch": Real_Epoch + 1, "Result_Error": '{:.6f}'.format(Result_Error),
              "Epoch_Error": loss_history,
              "Epoch_Duration": '{:.2f}'.format(Epoch_Duration)}
    # 数据库更新结果
    result_database = {"Model_ID": param.model_id, "Real_Epoch": Real_Epoch + 1,
                       "Result_Error": '{:.6f}'.format(Result_Error),
                       "Epoch_Error": str(loss_history),
                       "Epoch_Duration": '{:.2f}'.format(Epoch_Duration), "InputLayer_CoreNum": input_size,
                       "HideLayer_CoreNum": neurons_per_layer, "HideLayer_Num": hidden_layers,
                       "Output_Layer_CoreNum": output_size, "HideLayer_Active": activation_function,
                       "Learn_Rate": learn_rate, "Target_error": target_error, "MaxEpoch": epochs,
                       "WeightFileName": fileName}
    update_bpmodel_stucture(result_database)
    return success_res(result, SuccessCode.GET)


@model_router.post("/SOMFit", name="自组织拟合")
async def model_train(param: ModelIDParam, token=Header('token')):
    # 定义拟合方程
    # 2个输入参数
    def func2(X, a, b, v):
        ceos = [a, b]
        r = v  # 常量
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 3个输入参数
    def func3(X, a, b, c, v):
        ceos = [a, b, c]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 4个输入参数
    def func4(X, a, b, c, d, v):
        ceos = [a, b, c, d]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 5个输入参数
    def func5(X, a, b, c, d, e, v):
        ceos = [a, b, c, d, e]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 6个输入参数
    def func6(X, a, b, c, d, e, f, v):
        ceos = [a, b, c, d, e, f]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 7个输入参数
    def func7(X, a, b, c, d, e, f, g, v):
        ceos = [a, b, c, d, e, f, g]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 8个输入参数
    def func8(X, a, b, c, d, e, f, g, h, v):
        ceos = [a, b, c, d, e, f, g, h]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 9个输入参数
    def func9(X, a, b, c, d, e, f, g, h, i, v):
        ceos = [a, b, c, d, e, f, g, h, i]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 10个输入参数
    def func10(X, a, b, c, d, e, f, g, h, i, j, v):
        ceos = [a, b, c, d, e, f, g, h, i, j]
        r = v
        for i in range(len(inputParams)):
            if inputParams[i][5] == '线性':
                r += liner(X[i], ceos[i])
            elif inputParams[i][5] == '二次':
                r += quad(X[i], ceos[i])
            elif inputParams[i][5] == '三次':
                r += cube(X[i], ceos[i])
            elif inputParams[i][5] == '指数1':
                r += exponent(X[i], ceos[i])
            elif inputParams[i][5] == '指数2':
                r += exponent2(X[i], ceos[i])
            elif inputParams[i][5] == '幂函数':
                r += power(X[i], ceos[i])
            else:
                r += liner(X[i], ceos[i])
        return r

    # 获取数据
    X, Y = get_model_trainData(param.model_id)
    X_data = transform_Datasets(X)
    Y_data = transform_Datasets(Y)
    try:
        # 去除"Core_ID"和"Core_Name"这两个固定属性
        X_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict in X_data]
        # 构建通用的X_train张量
        # 获取属性顺序
        inputParams, outputParams = get_model_paras(param.model_id)
        keys =[item.Parameter_Ecode for item in  inputParams]
        # 确定数值属性的索引
        value_indices = [keys.index(key) for key in keys if key not in ['Core_ID', 'Core_Name']]
        # 提取数值部分
        values = [[item[key] for key in keys if key not in ['Core_ID', 'Core_Name']] for item in X_train_data]
        # 转换为ndarray
        X_train = np.array(values)[:, value_indices].transpose()
        # 去除"Core_ID"和"Core_Name"这两个固定属性
        Y_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict in Y_data]
        # 构建通用的Y_train张量
        Y_train = np.array([list(data_dict.values()) for data_dict in Y_train_data]).transpose()[0, :]
    except Exception as error:
        return error_res(ErrorCode.DATA_ERROR, "输入数据有误，请检查！")

    if len(inputParams) < 2 or len(inputParams) > 10:
        return error_res(ErrorCode.INPUTNUM_ERROR)
    starttime = time.time()
    try:
        # 拟合
        params, covariance = curve_fit(eval('func' + str(len(inputParams))), X_train, Y_train)
    except TypeError as e:
        return error_res(ErrorCode.SAMPLENUM_ERROR, "样本数据太少。无法拟合")
    except RuntimeError as e:
        return error_res(ErrorCode.CANT_ERROR)
    except Exception as e:
        return error_res(ErrorCode.DATA_ERROR, "输入数据有误，请检查！")

    # 计算训练时长
    endtime = time.time()
    dtime = endtime - starttime
    Epoch_Duration = "{:.3f}".format(dtime)
    # 计算误差
    params_list = params.tolist()
    y_fit = eval(f'func{str(len(inputParams))}(X_train,*params_list)')
    rmse = "{:.6f}".format(np.sqrt(np.mean((Y_train - y_fit) ** 2)))
    # 输出latex格式公式
    latex = "$$" + outputParams[0].Parameter_Ecode + "_{" + outputParams[0].Parameter_Name + "}=" + str(
        "{:.2f}".format(params[len(params) - 1]))
    for i in range(len(inputParams)):
        if inputParams[i][5] == '线性':
            if params[i] > 0:
                latex += "+" + liner_latex(inputParams[i][1], "{:.2f}".format(params[i]))
            else:
                latex += liner_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        elif inputParams[i][5] == '二次':
            if params[i] > 0:
                latex += "+" + quad_latex(inputParams[i][1], "{:.2f}".format(params[i]))
            else:
                latex += quad_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        elif inputParams[i][5] == '三次':
            if params[i] > 0:
                latex += "+" + cube_latex(inputParams[i][1], "{:.2f}".format(params[i]))
            else:
                latex += cube_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        elif inputParams[i][5] == '指数1':
            latex += "+" + exponent_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        elif inputParams[i][5] == '指数2':
            latex += "+" + exponent2_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        elif inputParams[i][5] == '幂函数':
            latex += "+" + power_latex(inputParams[i][1], "{:.2f}".format(params[i]))
        else:
            if params[i] > 0:
                latex += "+" + liner_latex(inputParams[i][1], "{:.2f}".format(params[i]))
            else:
                latex += liner_latex(inputParams[i][1], "{:.2f}".format(params[i]))
    latex += "$$"
    update_SOMmodel_structure(param.model_id,
                              {'Formula': latex, 'Result_Error': rmse, "Epoch_Duration": Epoch_Duration})
    # 预测测试集
    # 获取数据
    X, Y = get_model_testData(param.model_id)
    X_data = transform_Datasets(X)
    Y_data = transform_Datasets(Y)
    # 去除"Core_ID"和"Core_Name"这两个固定属性
    X_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict in X_data]
    # 构建通用的X_train张量
    # 获取属性顺序
    inputParams, outputParams = get_model_paras(param.model_id)
    keys = [item.Parameter_Ecode for item in inputParams]
    # 确定数值属性的索引
    value_indices = [keys.index(key) for key in keys if key not in ['Core_ID', 'Core_Name']]
    # 提取数值部分
    values = [[item[key] for key in keys if key not in ['Core_ID', 'Core_Name']] for item in X_train_data]
    # 转换为ndarray
    X_train = np.array(values)[:, value_indices].transpose()
    # 去除"Core_ID"和"Core_Name"这两个固定属性
    Y_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict in Y_data]
    # 构建通用的Y_train张量
    Y_train = np.array([list(data_dict.values()) for data_dict in Y_train_data]).transpose()[0, :]
    y_fit = eval(f'func{str(len(inputParams))}(X_train,*params_list)')
    y_ = y_fit.tolist()
    # 格式化输出
    train_data, test_data = get_model_SampleData(param.model_id)
    test_data = modified_data(test_data)
    inputParams, outputParams = get_model_paras(param.model_id)
    for index, item in enumerate(test_data):
        new_item = {}
        new_item['Core_ID'] = item['Core_ID']
        new_item['Core_Name'] = item['Core_Name']
        new_item['prediction'] = []
        for par_i, par in enumerate(outputParams):
            par_item = {}
            par_item['Parameter_Name'] = par.Parameter_Name
            par_item['value'] = item[par.Parameter_Ecode]
            par_item['prediction'] = '{:.2f}'.format(y_[index])
            new_item['prediction'].append(par_item)
        test_data[index] = new_item
    return success_res({'Formula': latex, 'Result_Error': rmse, "Epoch_Duration": Epoch_Duration,
                        "Create_DateTime": time.strftime("%Y-%m-%d %H:%M:%S", datetime.datetime.now().timetuple()),
                        "test_data": test_data},
                       SuccessCode.GET)


@model_router.post("/SOMParm", name="自组织参数")
async def model_train(param: ModelIDParam, token=Header('token')):
    som = get_sommodel_structure(param.model_id)
    return success_res(som, SuccessCode.GET)


@model_router.post("/trainParam", name="模型训练参数")
async def model_trainParam(param: ModelIDParam, token=Header('token')):
    train_param = get_model_train_para(param.model_id)
    return success_res(obj_to_dict(train_param), SuccessCode.GET)


@model_router.post("/self_evaluation", name="模型自评价")
async def model_predict(param: ModelIDParam, token=Header('token')):
    model = get_model(param.model_id)
    # BP模型
    if model.Model_Type == 1:
        X, Y = get_model_testData(param.model_id)
        X_Test, Y_Test = datasets_to_tensor(X, Y)
        # 创建BP神经网络，与之前定义的结构和参数相同
        structure = get_bpmodel_structure(param.model_id)
        if structure is None:
            return error_res(ErrorCode.GET, '当前模型没有训练，请先训练')
        bpnn = CustomizableBPNN(structure['InputLayer_CoreNum'], structure['Output_Layer_CoreNum'],
                                structure['HideLayer_Num'], structure['HideLayer_CoreNum'],
                                structure['HideLayer_Active'])
        # 加载模型权重
        bpnn.load_state_dict(torch.load('./weight/' + structure['WeightFileName'] + '.pth'))
        # 进行预测
        with torch.no_grad():
            predictions = bpnn(X_Test)
        # 格式化输出
        train_data, test_data = get_model_SampleData(param.model_id)
        test_data = modified_data(test_data)
        y_ = predictions.tolist()
        inputParams, outputParams = get_model_paras(param.model_id)
        for index, item in enumerate(test_data):
            new_item = {}
            new_item['Core_ID'] = item['Core_ID']
            new_item['Core_Name'] = item['Core_Name']
            new_item['prediction'] = []
            for par_i, par in enumerate(outputParams):
                par_item = {}
                par_item['Parameter_Name'] = par.Parameter_Name
                par_item['value'] = item[par.Parameter_Ecode]
                par_item['prediction'] = '{:.2f}'.format(y_[index][par_i])
                new_item['prediction'].append(par_item)
            test_data[index] = new_item
        return success_res(test_data, SuccessCode.GET)
        # BP模型


@model_router.post("/predictData", name="预测数据")
async def model_predictData(param: ModelList, token=Header('token')):
    # 排序
    modelList = sorted(param.model_list)
    data = objs_to_dict(get_model_data(modelList))
    modified_data = []
    for item in data:
        core_id = item["Core_ID"]
        core_name = item["Core_Name"]
        parameter_ecode = item["Parameter_Ecode"]
        parameter_value = item["Parameter_Value"]

        if core_id not in [x["Core_ID"] for x in modified_data]:
            modified_data.append({"Core_ID": core_id, "Core_Name": core_name})

        core_data = next((x for x in modified_data if x["Core_ID"] == core_id), None)
        core_data[parameter_ecode] = parameter_value
    return success_res(objs_to_dict(modified_data), SuccessCode.GET)


@model_router.post("/updatePredictData", name="更新预测数据")
async def model_predictData(param: ModelTrainDataUpdate, token=Header('token')):
    # 排序
    for item in param.add:
        item['Model_ID'] = sorted(item['Model_ID'])
    for item in param.delete:
        item['Model_ID'] = sorted(item['Model_ID'])
    add = update_model_predictData(param.add, param.delete, param.update)
    return success_res(add, SuccessCode.PUT)


@model_router.post("/predictDataAnalysis", name="模型预测数据分析")
async def model_predictData(param: ModelList, token=Header('token')):
    modelList = sorted(param.model_list)
    results = get_model_data(modelList)
    # 创建一个字典来保存参数的值
    param_values = defaultdict(list)
    # 遍历结果并整理数据
    for row in results:
        core_name = row.Core_Name
        parameter_name = row.Parameter_Name
        parameter_value = row.Parameter_Value
        if parameter_value is not None:
            param_values[parameter_name].append(float(parameter_value))

    # 计算每个参数的最大值、最小值和平均值
    param_stats = []
    for parameter_name, values in param_values.items():
        max_value = max(values)
        min_value = min(values)
        avg_value = sum(values) / len(values)
        values_array = np.array(values)
        variance = np.var(values_array)
        p10 = np.percentile(values_array, 10)
        p90 = np.percentile(values_array, 90)
        item = {
            "Parameter_Ecode": parameter_name,
            "Max_Value": max_value,
            "Min_Value": min_value,
            "Average_Value": '{:.2f}'.format(avg_value),
            "Variance": '{:.2f}'.format(variance),
            "P10": '{:.2f}'.format(p10),
            "P90": '{:.2f}'.format(p90)
        }
        param_stats.append(item)
    return success_res(param_stats, SuccessCode.GET)


@model_router.post("/predict", name="模型预测")
async def model_predict(param: ModelList, token=Header('token')):
    # BP神经网络模型
        # 获取模型复合数据集
        X, Y = get_model_predictData(sorted(param.model_list))
        # 遍历不同模型并训练
        prediction = []
        for item in param.model_list:
            model = get_model(item)
            # 获取属于该模型的参数
            inputParams, outputParams = get_model_paras(item)
            # 根据不同模型不同参数分割数据集
            params = [item.Parameter_Ecode for item in inputParams + outputParams]
            X_Test, Y_Test = datasets_to_tensor_split(X, Y, params)
            if model.Model_Type == 1:
                # 创建BP神经网络，与之前定义的结构和参数相同
                structure = get_bpmodel_structure(item)
                if structure is None:
                    return error_res(ErrorCode.GET, '当前存在未训练模型，请先训练')
                bpnn = CustomizableBPNN(structure['InputLayer_CoreNum'], structure['Output_Layer_CoreNum'],
                                        structure['HideLayer_Num'], structure['HideLayer_CoreNum'],
                                        structure['HideLayer_Active'])
                # 加载模型权重
                try:
                    bpnn.load_state_dict(torch.load('./weight/' + structure['WeightFileName'] + '.pth'))
                except Exception as e:
                    return error_res(ErrorCode.GET, '找不到权重文件，请重新训练')
                # 进行预测
                with torch.no_grad():
                    predictions = bpnn(X_Test)
                # 暂时存储结果
                tmp = {}
                tmp['Model_Name'] = model.Model_Name
                tmp['prediction'] = predictions.tolist()
                prediction.append(tmp)
            if model.Model_Type == 2:
                # 定义拟合方程
                # 2个输入参数
                def func2(X, a, b, v):
                    ceos = [a, b]
                    r = v  # 常量
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 3个输入参数
                def func3(X, a, b, c, v):
                    ceos = [a, b, c]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 4个输入参数
                def func4(X, a, b, c, d, v):
                    ceos = [a, b, c, d]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 5个输入参数
                def func5(X, a, b, c, d, e, v):
                    ceos = [a, b, c, d, e]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 6个输入参数
                def func6(X, a, b, c, d, e, f, v):
                    ceos = [a, b, c, d, e, f]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 7个输入参数
                def func7(X, a, b, c, d, e, f, g, v):
                    ceos = [a, b, c, d, e, f, g]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 8个输入参数
                def func8(X, a, b, c, d, e, f, g, h, v):
                    ceos = [a, b, c, d, e, f, g, h]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 9个输入参数
                def func9(X, a, b, c, d, e, f, g, h, i, v):
                    ceos = [a, b, c, d, e, f, g, h, i]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 10个输入参数
                def func10(X, a, b, c, d, e, f, g, h, i, j, v):
                    ceos = [a, b, c, d, e, f, g, h, i, j]
                    r = v
                    for i in range(len(inputParams)):
                        if inputParams[i][5] == '线性':
                            r += liner(X[i], ceos[i])
                        elif inputParams[i][5] == '二次':
                            r += quad(X[i], ceos[i])
                        elif inputParams[i][5] == '三次':
                            r += cube(X[i], ceos[i])
                        elif inputParams[i][5] == '指数1':
                            r += exponent(X[i], ceos[i])
                        elif inputParams[i][5] == '指数2':
                            r += exponent2(X[i], ceos[i])
                        elif inputParams[i][5] == '幂函数':
                            r += power(X[i], ceos[i])
                        else:
                            r += liner(X[i], ceos[i])
                    return r

                # 获取数据
                # X_data = transform_Datasets(X_Test)
                # Y_data = transform_Datasets(Y_Test)
                # # 去除"Core_ID"和"Core_Name"这两个固定属性
                # X_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict
                #                 in X_data]
                # # 构建通用的X_train张量
                # # 获取属性顺序
                # inputParams, outputParams = get_model_paras(param.model_id)
                # keys = [item.Parameter_Ecode for item in inputParams]
                # # 确定数值属性的索引
                # value_indices = [keys.index(key) for key in keys if key not in ['Core_ID', 'Core_Name']]
                # # 提取数值部分
                # values = [[item[key] for key in keys if key not in ['Core_ID', 'Core_Name']] for item in X_train_data]
                # 转换为ndarray
                # X_train = np.array(values)[:, value_indices].transpose()
                X_train = np.array(X_Test).transpose()
                # 去除"Core_ID"和"Core_Name"这两个固定属性
                # Y_train_data = [{k: v for k, v in data_dict.items() if k not in ['Core_ID', 'Core_Name']} for data_dict
                #                 in Y_data]
                # # 构建通用的Y_train张量
                # Y_train = np.array([list(data_dict.values()) for data_dict in Y_train_data]).transpose()[0, :]
                Y_train = np.array(Y_Test).transpose()[0, :]

                if len(inputParams) < 2 or len(inputParams) > 10:
                    return error_res(ErrorCode.INPUTNUM_ERROR)
                try:
                    # 拟合
                    params, covariance = curve_fit(eval('func' + str(len(inputParams))), X_train, Y_train)
                except TypeError as e:
                    return error_res(ErrorCode.SAMPLENUM_ERROR, "样本数据太少。无法拟合")
                except RuntimeError as e:
                    return error_res(ErrorCode.CANT_ERROR)
                params_list = params.tolist()
                y_fit = eval(f'func{str(len(inputParams))}(X_train,*params_list)')
                y_fit = y_fit.reshape(-1, 1)
                # 暂时存储结果
                tmp = {}
                tmp['Model_Name'] = model.Model_Name
                tmp['prediction'] = y_fit.tolist()
                prediction.append(tmp)
        # 格式化输出
        test_data = objs_to_dict(get_model_data(sorted(param.model_list)))
        test_data = modified_data(test_data)
        for index, item in enumerate(test_data):
            new_item = {}
            new_item['Core_ID'] = item['Core_ID']
            new_item['Core_Name'] = item['Core_Name']
            new_item['prediction'] = []
            for par_i, par in enumerate(outputParams):
                par_item = {}
                par_item['Parameter_Name'] = par.Parameter_Name
                par_item['value'] = item[par.Parameter_Ecode]
                # 各个模型预测结果
                model_preList = []
                for pre in prediction:
                    model_predict = {}
                    model_predict['Model_Name'] = pre['Model_Name']
                    model_predict['prediction'] = pre['prediction'][index][par_i]
                    model_preList.append(model_predict)
                par_item['prediction'] = model_preList
                new_item['prediction'].append(par_item)
            test_data[index] = new_item
        return success_res(test_data, SuccessCode.GET)

